package com.parking.mgr.useraccount.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.parking.mgr.common.constant.CommonConstant;
import com.parking.mgr.common.context.constant.ConstantContext;
import com.parking.mgr.common.enums.CommonStatusEnum;
import com.parking.mgr.common.exception.ServiceException;
import com.parking.mgr.common.exception.enums.AuthExceptionEnum;
import com.parking.mgr.common.pojo.login.LoginUserInfo;
import com.parking.mgr.common.util.IdUtils;
import com.parking.mgr.common.util.MD5Util;
import com.parking.mgr.common.util.TimestampUtil;
import com.parking.mgr.useraccount.facede.LoginServiceFacede;
import com.parking.mgr.useraccount.model.entity.*;
import com.parking.mgr.useraccount.model.mapper.LoginPasswordMapper;
import com.parking.mgr.useraccount.model.mapper.LoginTokenMapper;
import com.parking.mgr.useraccount.model.mapper.UserAccountMapper;
import com.parking.mgr.useraccount.model.mapper.UserInfoMapper;
import com.parking.mgr.useraccount.model.param.LoginTokenParam;
import com.parking.mgr.useraccount.model.param.SysLoginParam;
import com.parking.mgr.useraccount.service.AuthService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Objects;

/**
 * @ClassName AuthServiceImpl
 * @Description TODO
 * @Auther: zhijianpu
 * @Date: 2023/5/24
 */
@Log4j2
@Service
public class AuthServiceImpl extends ServiceImpl<UserAccountMapper, UserAccountEntity> implements AuthService, UserDetailsService{

    @Resource
    private LoginServiceFacede loginServiceFacede;

    @Resource
    private UserAccountMapper userAccountMapper;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private LoginPasswordMapper loginPasswordMapper;

    @Resource
    private LoginTokenMapper loginTokenMapper;


    static final String TEST_PWD = "999999";
    private SysLoginParam sysLoginParam;
    private HttpServletRequest request;

    @Override
    public String createClientId(HttpServletRequest request) {
        String clientId = request.getHeader(CommonConstant.CLIENT_ID);
        // 判断clientId是否为空
        LoginTokenParam loginTokenParam = new LoginTokenParam();
        if (StringUtils.isNotEmpty(clientId)) {
            // 客户端ID存在，获取token实体
            LoginTokenEntity loginTokenEntity = loginTokenMapper.selectOne(new LambdaQueryWrapper<LoginTokenEntity>()
                    .eq(LoginTokenEntity::getFClientId, clientId));
            if (ObjectUtil.isNull(loginTokenEntity)) {
                // 无token信息，生成token
                clientId = IdUtils.getId();
                loginTokenParam.setFClientId(clientId);
                loginServiceFacede.add(loginTokenParam, request);
            } else {
                // 如果找到client_id，延期15天
                Long fExpireTimeMillis = loginTokenEntity.getFExpireTimeMillis() + TimestampUtil.extensionDays();
                clientId = loginTokenEntity.getFClientId();
                loginTokenParam.setFClientId(clientId);
                loginTokenParam.setFExpireTimeMillis(fExpireTimeMillis);
                loginServiceFacede.updateExpireTime(loginTokenParam);
            }
        } else {
            // 客户端ID不存在，生成新的客户端ID
            clientId = IdUtils.getId();
            loginTokenParam.setFClientId(clientId);
            loginServiceFacede.add(loginTokenParam, request);
        }

        return clientId;
    }

    @Override
    public String login(SysLoginParam sysLoginParam, HttpServletRequest request) {
        String clientId =  request.getHeader(CommonConstant.CLIENT_ID);
        this.sysLoginParam = sysLoginParam;
        this.request = request;
        log.info("账号密码登录");
        // 获取图片验证码
        //String inputCode = sysLoginParam.getFCode();
        //String authCodeId = sysLoginParam.getId();
        //log.info("1. 获取数据库验证码");
        //String authCode = loginServiceFacede.getAuthCodeById(authCodeId);
        //BasicAuthCodeEntity authCodeEntity = loginServiceFacede.getAuthCodeEntityByClientId(clientId);
        // 验证码
        //String authCode = authCodeEntity.getFAuthCode();
        String phoneNo = sysLoginParam.getPhone();
        String password = sysLoginParam.getFPassword();
        log.info("2. 账号或密码为空判断");
        // 国际化改造
        if (CollectionUtil.isEmpty(Arrays.asList(phoneNo, password))) {
            throw new ServiceException(AuthExceptionEnum.ACCOUNT_PWD_EMPTY.getMessage());
        }
        //AuthExceptionEnum.ACCOUNT_PWD_EMPTY.assertNotEmpty(Arrays.asList(account, password, clientId));
        UserInfoEntity userInfoEntity = loadUserFromDb(phoneNo);

        // 获取 LoginPasswordEntity 对象
        LoginPasswordEntity loginPasswordEntity = this.getLoginPasswordEntity(userInfoEntity.getFkUserPassportId());
        // 数据库中存储的密码和salt
        String dbPasswordMD5 = loginPasswordEntity.getFPassword();
        String passSalt = loginPasswordEntity.getFPasssalt();
        // 国际化改造
        if (StrUtil.isEmpty(dbPasswordMD5)) {
            throw new ServiceException(AuthExceptionEnum.ACCOUNT_PWD_ERROR.getMessage());
        }
        // AuthExceptionEnum.ACCOUNT_PWD_ERROR.assertNotBlank(dbPasswordMD5);
        // 提交的密码 MD5
        String inputPwdMD5 = MD5Util.password(password, passSalt);
        //AuthExceptionEnum.ACCOUNT_PWD_ERROR.assertTrue(Objects.equals(dbPasswordMD5, inputPwdMD5));
        // 国际化改造
        if (!MD5Util.compare(password, passSalt, dbPasswordMD5)) {
            throw new ServiceException(AuthExceptionEnum.ACCOUNT_PWD_ERROR.getMessage());
        }
        // AuthExceptionEnum.ACCOUNT_PWD_ERROR.assertTrue(MD5Util.compare(password, passSalt, dbPasswordMD5));

        return doLogin(userInfoEntity, phoneNo, clientId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String logout(HttpServletRequest request) {
        // 获取token
        String token = request.getHeader(CommonConstant.LOGIN_TOKEN);
        String clientId = request.getHeader(CommonConstant.CLIENT_ID);

//            // 如果token为空直接返回
//            if (ObjectUtil.isEmpty(token)) {
//                return;
//            }

        LoginTokenEntity loginTokenEntity = loginServiceFacede.getLoginTokenEntity(token, clientId);

        // 删除token信息
        loginServiceFacede.delete(token, clientId);

        // 生成新的clientId
        clientId = IdUtils.getId();
        LoginTokenParam loginTokenParam = new LoginTokenParam();
        loginTokenParam.setFClientId(clientId);
        loginServiceFacede.add(loginTokenParam, request);

        //根据token获取JwtPayLoad部分
        //JwtPayLoad jwtPayLoad = JwtTokenUtil.getJwtPayLoad(token);

        //获取缓存的key
        //String loginUserCacheKey = jwtPayLoad.getUuid();
        //this.clearUser(loginUserCacheKey);

        return clientId;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String logoutByTokenClientId(String loginToken, String clientId) {
        // 获取token

//            // 如果token为空直接返回
//            if (ObjectUtil.isEmpty(token)) {
//                return;
//            }

        LoginTokenEntity loginTokenEntity = loginServiceFacede.getLoginTokenEntity(loginToken, clientId);

        // 删除token信息
        loginServiceFacede.delete(loginToken, clientId);

        // 生成新的clientId
        clientId = IdUtils.getId();
        LoginTokenParam loginTokenParam = new LoginTokenParam();
        loginTokenParam.setFClientId(clientId);
        loginServiceFacede.add(loginTokenParam, request);

        //根据token获取JwtPayLoad部分
        //JwtPayLoad jwtPayLoad = JwtTokenUtil.getJwtPayLoad(token);

        //获取缓存的key
        //String loginUserCacheKey = jwtPayLoad.getUuid();
        //this.clearUser(loginUserCacheKey);

        return clientId;

    }

    /**
     * 该方法已迁移到工具类
     */
//    @Override
//    public String getTokenFromRequest(HttpServletRequest request) {
//        String authToken = request.getHeader(CommonConstant.AUTHORIZATION);
//        if (ObjectUtil.isEmpty(authToken) || CommonConstant.UNDEFINED.equals(authToken)) {
//            return null;
//        } else {
//            //token不是以Bearer打头，则响应回格式不正确
//            if (!authToken.startsWith(CommonConstant.TOKEN_TYPE_BEARER)) {
//                throw new AuthException(AuthExceptionEnum.NOT_VALID_TOKEN_TYPE);
//            }
//            try {
//                authToken = authToken.substring(CommonConstant.TOKEN_TYPE_BEARER.length() + 1);
//            } catch (StringIndexOutOfBoundsException e) {
//                throw new AuthException(AuthExceptionEnum.NOT_VALID_TOKEN_TYPE);
//            }
//            // 判断是否开启了加密
//            if (ConstantContextHolder.getCryptogramConfigs().getTokenEncDec()) {
//                // 解密token
//                try {
//                    authToken = CryptogramUtil.doDecrypt(authToken);
//                } catch (Exception e) {
//                    throw new AuthException(AuthExceptionEnum.LOGIN_EXPIRED);
//                }
//            }
//        }
//        return authToken;
//    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return null;
    }

    /**
     * 通过账号获取用户实体
     *
     * @param account 账号
     * @return
     */
    private UserInfoEntity loadUserFromDb(String account) {

        UserAccountEntity userAccountEntity = userAccountMapper.selectOne(new LambdaQueryWrapper<UserAccountEntity>()
                .eq(UserAccountEntity::getFAccount, account)
                .ne(UserAccountEntity::getFEnableState, CommonStatusEnum.DISABLE.getCode()));


        return userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfoEntity>()
                .eq(UserInfoEntity::getFkUserPassportId, userAccountEntity.getFkUserPassportId())
                .ne(UserInfoEntity::getFEnableState, CommonStatusEnum.DISABLE.getCode()));
    }

    private String doLogin(UserInfoEntity userInfoEntity, String phoneNo, String clientId) {
        String userInfoEnableState = userInfoEntity.getFEnableState();
        // 验证账号是否被冻结
        // 构造LoginUserInfo
        LoginUserInfo loginUserInfo = this.genLoginUserInfo(userInfoEntity, phoneNo);
        // 构造jwtPayLoad
        //JwtPayLoad jwtPayLoad = new JwtPayLoad(userInfoEntity.getFkUserPassportId(), account);
        // 生成token
        //String token = JwtTokenUtil.generateToken(jwtPayLoad);
        String token = IdUtils.getSimpleId() + IdUtils.getSimpleId();
        // 缓存token与登录用户信息对应，默认2个小时
        //this.cacheLoginUser(jwtPayLoad, loginUserInfo);
        // 更新token
        //loginServiceFacede.updateToken(token, userInfoEntity.getFkUserPassportId(), clientId);
        LoginTokenEntity loginTokenEntity = new LoginTokenEntity();
        loginTokenEntity.setId(IdUtils.getId());
        loginTokenEntity.setFkUserPassportId(userInfoEntity.getFkUserPassportId());
        loginTokenEntity.setFClientId(clientId);
        loginTokenEntity.setFLoginToken(token);
        loginServiceFacede.saveLoginToken(loginTokenEntity);
        // 更新登录授权
        //loginAuthService.saveOrUpdateAuth(userInfoEntity.getFkUserPassportId(), clientId, token, imageCode);
        // //登录成功，设置SpringSecurityContext上下文，方便获取用户
        this.setSpringSecurityContextAuthentication(loginUserInfo);
        // 返回 token
        return token;
    }


    private LoginUserInfo genLoginUserInfo(UserInfoEntity userInfoEntity, String phoneNo) {
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        BeanUtil.copyProperties(userInfoEntity, loginUserInfo);
        loginUserInfo.setFAccount(phoneNo);
        loginUserInfo.setFkUserPassportId(userInfoEntity.getFkUserPassportId());
        loginUserInfo.setFMobilePhone(phoneNo);

        return loginUserInfo;
    }

//    private void cacheLoginUser(JwtPayLoad jwtPayLoad, LoginUserInfo loginUserInfo) {
//        String redisLoginUserKey = jwtPayLoad.getUuid();
//        // 提前5分钟过期
//        Long expireTime = ConstantContextHolder.getTokenExpireSec() - 5 * 60;
//        //loginUserCache.put(redisLoginUserKey, loginUserInfo, expireTime);
//    }

    @Override
    public void setSpringSecurityContextAuthentication(LoginUserInfo loginUserInfo) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(
                        loginUserInfo,
                        null,
                        loginUserInfo.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
    }



    private LoginPasswordEntity getLoginPasswordEntity(String userPassportId) {

        return loginPasswordMapper.selectOne(new LambdaQueryWrapper<LoginPasswordEntity>()
                .eq(LoginPasswordEntity::getFkUserPassportId, userPassportId)
                .ne(LoginPasswordEntity::getFEnableState, CommonStatusEnum.DISABLE.getCode()));
    }

    /**
     * 根据key清空登陆信息
     *
     * @param loginUserKey
     */
    private void clearUser(String loginUserKey) {
        // 获取缓存的用户
        //Object cacehObject = loginUserCache.get(loginUserKey);

        // 如果缓存的用户存在，清除会话，否则标识该会话信息已失效，不执行任何操作
//        if (ObjectUtil.isNotEmpty(cacehObject)) {
//            // 清除登录会话
//            loginUserCache.remove(loginUserKey);
//        }
    }

}
